/** @file    SocketUdpLS.cpp
 *  @author  Alessandro Polo
 *  @version $Id: WebDownloaderCurl.cpp 2891 2010-08-12 01:58:59Z alex $
 *  @brief
 * File containing methods for the wosh::network::libsockets::SocketUdpLS class.
 * The header for this class can be found in SocketUdpLS.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/
 
 #include <framework/network/libcurl/WebDownloaderCurl.h>
 #include <core/ObjectAllocator.h>
 #include <framework/network/libcurl/libcurlInit.h>
 #include <curl/curl.h>


#ifdef _WebDownloader_DEBUG_ENABLED
# define LOG( LEVEL, FORMAT, ... ) Log(LEVEL, FORMAT, __VA_ARGS__);
#else
# define LOG( LEVEL, FORMAT, ... ) ;
#endif


using namespace std;
namespace wosh {
 namespace network {
  namespace libcurl {

 WOSH_REGISTER(wosh::network::libcurl::WebDownloaderCurl, "wosh::network::WebDownloader", 1.0, _static_WebDownloaderCurl )

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

WebDownloaderCurl::WebDownloaderCurl( )
	: wosh::network::WebDownloader() {
	this->local_file = NULL;
	LOG(LOG_DEBUG, ": Initializing [%s]..", curl_version() );
	libcurl_global_initialization();
	this->curl_handler = curl_easy_init();
	LOG(LOG_DEBUG, ": Initialized, got handler %p..", this->curl_handler );
 }

WebDownloaderCurl::~WebDownloaderCurl() {
	if ( this->local_file != NULL ) {
		fclose(this->local_file); this->local_file = NULL;
	 }
	// always cleanup
	LOG(LOG_DEBUG, ":setUrl() : Calling curl_easy_cleanup(%p)..", this->curl_handler );
	curl_easy_cleanup(this->curl_handler);
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT WebDownloaderCurl::setUrl( const std::string& base_url ) {
	if ( this->curl_handler == NULL ) {
		LOG(LOG_CRITICAL, ":setUrl(%s) FAILED : NULL curl-hanlder!", base_url.c_str() );
		return -1;
	 }
	// First set the URL that is about to receive our POST. This URL can
	// just as well be a https:// URL if that is what should receive the data.
	LOG(LOG_DEBUG, ":setUrl(%s) : Calling curl_easy_setopt(%p)..", base_url.c_str(), this->curl_handler );
	CURLcode res = curl_easy_setopt(this->curl_handler, CURLOPT_URL, base_url.c_str() );
	return (WRESULT)res;
 }

// If you're using libcurl as a win32 DLL, you MUST use the CURLOPT_WRITEFUNCTION if you set CURLOPT_WRITEDATA - or you will experience crashes. 
// ??
WRESULT WebDownloaderCurl::setTargetFile( const std::string& local_filename ) {
	if ( this->curl_handler == NULL ) {
		LOG(LOG_CRITICAL, ":setUrl(%s) FAILED : NULL curl-hanlder!", local_filename.c_str() );
		return WRET_ERR_ILLEGAL_USE;
	 }
	if ( this->local_file != NULL ) {
		fclose(this->local_file); this->local_file = NULL;
	 }
	this->local_file = fopen(local_filename.c_str() , "w");
	if ( this->local_file == NULL ) {
		LOG(LOG_CRITICAL, ":setUrl(%s) FAILED : Can't open file for read!", local_filename.c_str() );
		return WRET_ERR_PARAM;
	 }
	// First set the URL that is about to receive our POST. This URL can
	// just as well be a https:// URL if that is what should receive the data.
	LOG(LOG_DEBUG, ":setTargetFile(%s) : Calling curl_easy_setopt(%p)..", local_filename.c_str(), this->curl_handler );
	CURLcode res = curl_easy_setopt(this->curl_handler, CURLOPT_FILE, this->local_file );
	return (WRESULT)res;
 }

WRESULT WebDownloaderCurl::setOption( OPTION option, const Variant& value ) {
	if ( this->curl_handler == NULL ) {
		LOG(LOG_CRITICAL, ":setOption(%d,%s) FAILED : NULL curl-hanlder!", (int)option, value.toString().c_str() );
		return WRET_ERR_ILLEGAL_USE;
	 }
	CURLcode res = CURLE_OK;
	LOG(LOG_DEBUG, ":setOption(%d,%s) : Calling curl_easy_setopt(%p)..", (int)option, value.toString().c_str(), this->curl_handler );
	switch(option) {
		case OPTION_URL:
			res = curl_easy_setopt(this->curl_handler, CURLOPT_URL, value.toString().c_str() );
			break;
		case OPTION_USERPWD:
			res = curl_easy_setopt(this->curl_handler, CURLOPT_USERPWD, value.toString().c_str() );
			break;
		case OPTION_PROXYUSERPWD:
			res = curl_easy_setopt(this->curl_handler, CURLOPT_PROXYUSERPWD, value.toString().c_str() );
			break;
		case OPTION_POST:
			res = curl_easy_setopt(this->curl_handler, CURLOPT_POST, (long)value.toBoolean(true) );
			break;
		case OPTION_POSTFIELDS:
			res = curl_easy_setopt(this->curl_handler, CURLOPT_POSTFIELDS, value.toString().c_str() );
			break;
		case OPTION_FOLLOWLOCATION:
			res = curl_easy_setopt(this->curl_handler, CURLOPT_FOLLOWLOCATION, (long)value.toBoolean(true) );
			break;
		default:
			return -1;
	 }
	return (WRESULT)res;
 }

WRESULT WebDownloaderCurl::clearOptions() {
	if ( this->local_file != NULL ) {
		fclose(this->local_file); this->local_file = NULL;
	 }
	if ( this->curl_handler == NULL ) {
#ifdef _WebDownloader_DEBUG_ENABLED
		Log(LOG_CRITICAL, ":clearOptions() FAILED : NULL curl-hanlder!" );
#endif
		return WRET_ERR_ILLEGAL_USE;
	 }
	LOG(LOG_DEBUG, ":clearOptions() : Calling curl_easy_reset(%p)..", this->curl_handler );
	curl_easy_reset(this->curl_handler);
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT WebDownloaderCurl::execute() {
	if ( this->curl_handler == NULL ) {
#ifdef _WebDownloader_DEBUG_ENABLED
		Log(LOG_CRITICAL, ":execute() FAILED : NULL curl-hanlder!" );
#endif
		return -1;
	 }
	// Perform the request, res will get the return code
	LOG(LOG_DEBUG, ":execute() : Calling curl_easy_perform(%p)..", this->curl_handler );
	CURLcode res = curl_easy_perform(this->curl_handler);
	LOG(LOG_INFO, ":execute() returned %d", (int)res );
	return (WRESULT)res;
 }

WRESULT WebDownloaderCurl::download( const std::string& base_url, const std::string& local_filename ) {
	if ( this->curl_handler == NULL ) {
		LOG(LOG_CRITICAL, ":download(%s,%s) FAILED : NULL curl-hanlder!", base_url.c_str(), local_filename.c_str() );
		return WRET_ERR_ILLEGAL_USE;
	 }
	clearOptions();
	WRESULT ret;
	ret = setUrl( base_url );
	if ( WFAILED(ret) ) {
		return WRET_ERR_PARAM;
	 }
	ret = setTargetFile( local_filename );
	if ( WFAILED(ret) ) {
		return WRET_ERR_PARAM;
	 }
	ret = execute();
	return ret;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////



//////////////////////////////////////////////////////////////////////////////////////////////////////////////

  }; // namespace libsockets
 }; // namespace network
}; // namespace wosh
